// Copyright (C) Calum Grant 2008

#include "cppscript"
#include "dynamic/extensions.hpp"
#include <stdlib.h>


static_call( register_pickle_type( cmp_int, 0 ) );
static_call( register_pickle_type( cmp_char, ' ' ) );
static_call( register_pickle_type( cmp_wchar, L' ') );


void dynamic::write_int_to_stream(wostream & os, int i)
{
#if defined(__CYGWIN__)
	const int buf_size=40;
	char buf[buf_size];
	snprintf(buf, buf_size, "%d", i);
	for(char *i=buf; *i; ++i) os << wchar_t(*i);
#else
	// Bizarrely this does not work on cygwin
	os << i;
#endif
}


namespace
{
	void write_to_stream(wostream & os, int i)
	{
		write_int_to_stream(os, i);
	}

	template<typename Int>
	void write_to_stream(ostream & os, Int i)
	{
		os << i;
	}

	template<typename Int>
	void write_to_stream(wostream & os, Int i)
	{
		os << i;
	}


	template<typename>
	struct int_type;

	template<> struct int_type<char>
	{
		static const var_cmp_index value = cmp_char;
		static const char * class_name() { return "char"; }
	};

	template<> struct int_type<wchar_t>
	{
		static const var_cmp_index value = cmp_wchar;
		static const char * class_name() { return "char"; }
	};

	template<> struct int_type<int>
	{
		static const var_cmp_index value = cmp_int;
		static const char * class_name() { return "int"; }
	};
}

namespace dynamic
{
	namespace types
	{
	/// Implements an integer variable.
	/** @param Ch is the represented type. 
		\ingroup impl */
	template<typename Ch>
	class int_impl : public dynamic::var_impl
	{
		int i;
	public:
		typedef Ch char_type;

		int_impl(Ch i) : i(i) { }

		void output(ostream & ss)
		{
			write_to_stream(ss, Ch(i));
		}

		void output(wostream & os)
		{
			write_to_stream(os, Ch(i));
		}

		int as_int()
		{
			return i;
		}

		double as_double()
		{
			return i;
		}

		var op_mul(const var & v)
		{
			return i * v.as_int();
		}

		var op_div(const var & v)
		{
			return i / v.as_int();
		}

		var op_mod(const var & v)
		{
			return i % v.as_int();
		}

		var_cmp_index comparison_index() 
		{ 
			return int_type<Ch>::value; 
		}

		var_cmp_result compare2(const var & v)
		{
			int diff = i - v.as_int();
			if(diff<0) return cmp_lt;
			if(diff>0) return cmp_gt;
			return cmp_equal;
		}

		void op_inc()
		{
			++i;
		}

		void op_dec()
		{
			--i;
		}

		var op_and(const var &a)
		{
			return i & a.as_int();
		}

		var op_or(const var & a)
		{
			return i | a.as_int();
		}

		var op_xor(const var & a)
		{
			return i ^ a.as_int();
		}

		var op_inv()
		{
			return ~i;
		}

		bool as_bool()
		{
			return i!=0;
		}

		void copy_to(void * dest) const
		{
			new(dest) int_impl(*this);
		}

		var op_lshift(const var & a)
		{
			return i << a.as_int();
		}

		var op_rshift(const var & a)
		{
			return i >> a.as_int();
		}

		var op_neg()
		{
			return -i;
		}

		void assign_add(const var &a)
		{
			int ai = a.as_int();
			i += ai;
		}

		void assign_sub(const var &a)
		{
			int ai = a.as_int();
			i -= ai;
		}

		void assign_mul(const var &a)
		{
			int ai = a.as_int();
			i *= ai;
		}

		void assign_div(const var &a)
		{
			int ai = a.as_int();
			i /= ai;
		}

		void assign_mod(const var &a)
		{
			int ai = a.as_int();
			i %= ai;
		}

		void assign_lshift(const var &a)
		{
			int ai = a.as_int();
			i <<= ai;
		}

		void assign_rshift(const var &a)
		{
			int ai = a.as_int();
			i >>= ai;
		}

		void assign_and(const var &a)
		{
			int ai = a.as_int();
			i &= ai;
		}

		void assign_or(const var &a)
		{
			int ai = a.as_int();
			i |= ai;
		}

		void assign_xor(const var &a)
		{
			int ai = a.as_int();
			i ^= ai;
		}

		void unpickle(unpickler & u)
		{
			i = u.read_int();
		}

		std::string class_name() 
		{ 
			return int_type<Ch>::class_name(); 
		}

		var op_add(const var & v)
		{
			return Ch(i + v.as_int());
		}

		var op_sub(const var & v)
		{
			return Ch(i - v.as_int());
		}

		var as_root()
		{
			return Ch(i);
		}

		var as_nonroot()
		{
			return Ch(i);
		}

		void pickle(pickler & p)
		{
			p.write_object_type(int_type<Ch>::value);
			p.write_int(i);
		}

		void get_range(std::size_t container_size, std::pair<std::size_t,std::size_t> & out)
		{
			out.first = i<0 ? container_size+i : i;
			out.second=out.first+1;
		}
	};
	}
}


/// \todo Want a 64-bit integer

var::var(int i) : m_variant(types::int_impl<int>(i)) { }

var::var(char ch) : m_variant(types::int_impl<char>(ch)) { }

var::var(wchar_t wc) : m_variant(types::int_impl<wchar_t>(wc)) { }
